﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;


using System.Diagnostics;


using SpiritEngine.Source.ApplicationLayer;
using SpiritEngine.Source.EngineCore;
using SpiritEngine.Source.Utilities;


namespace SpiritEngine.Source.EngineCore
{
    #region Globals

    #endregion


    /// <summary>
    /// This class provides logging functionality to the SpiritEngine.
    /// </summary>
    public class Logger: SE_Base, ISE_Logger
    {
        #region Constants/Enums

        /// <summary>
        /// These flags are used to set the OutputModes flags property to tell the Logger which output modes to use.
        /// Multiple flags can be specified.  If all flags are off, then the logger will use the auto output mode.
        /// </summary>
        public enum OutputModes
        {
            NONE = 0,              // If the OutputModes property is equal to this value, then logging is disabled.
            CONSOLE_WINDOW  = 1,   // Output to the Console window (if the Engine is being used by a console app)
            STANDARD_ERROR_STREAM = 2,  // Output to the standard error stream.
            VS_OUTPUT_PANE = 4,    // Output to Visual Studio's output pane.
            LOG_FILE = 8,          // Output to the log file.
            ALL = CONSOLE_WINDOW | STANDARD_ERROR_STREAM | VS_OUTPUT_PANE | LOG_FILE,   // Use all of the output modes.
            MAX = ALL   // The largest valid value for the OutputMode property.
        }

        #endregion


        #region Structs

        #endregion


        #region Static Member Variables and Methods

        #endregion


        #region Member Variables

        // ========================================================================================================================================================================================================
        // Property Variables
        // ========================================================================================================================================================================================================

        bool m_AddTimeStamps; // Determines whether the logger will add a time stamp to each message.
        char m_OutputFlags;  // Determines which output modes the logger should use.


        // These are private to force classes that inherit from this class to declare their own member variable for use by the corresponding properties.
        // This way, for example, we can keep track of initialization state for each step in the inheritence chain.  So if the base class is initialized, but
        // the subclass is not, then the base class's IsInitialized property would return true, but the subclass's IsInitialized property would return false.
        private int m_SEObjectFlags = 0;    // Stores object flags defined by the SEObject_Flags enum.

        #endregion
         

        #region Events/Delegates

        #endregion


        #region Constructors and Destructors (aka Finalizers)

        public Logger(ISE_EngineCore parent)
        {
            if (parent == null)
                throw new ArgumentException("Logger(ISE_EngineCore): The parent parameter cannot be null!");


            m_Parent = parent;
        }

        #endregion


        #region Non-Public Methods

        // This section is for methods that are declared as private, protected, protected internal, or internal.

        #endregion


        #region Public Methods

        /// <summary>
        /// Initializes this object.
        /// </summary>
        /// <param name="paramsList">A variable length parameter list.
        /// 
        /// EXPECTED PARAMETERS:
        /// None</param>
        /// <returns>True if successful or false otherwise.</returns>
        public override bool Initialize(params object[] paramsList)
        {
            base.Initialize();


#if VERIFY_PARAMS
            if (!SE_Debug.VerifyParameters(paramsList))
            {
                throw new ArgumentException("Logger.Initialize(object[]): Invalid params!");
            }
#endif


            m_OutputFlags = (char) OutputModes.CONSOLE_WINDOW;

            // Set the initialized flag to indicate that this object has been initialized.
            m_SEObjectFlags |= (int)SEObject_Flags.Initialized;

            return true;
        }


        /// <summary>
        /// This function is used to log a message.
        /// </summary>
        /// <param name="message">The text to write into the log file as a new line.</param>
        public void Log(string message)
        {
            if (m_OutputFlags == (int) OutputModes.NONE)
                return;



            if (m_AddTimeStamps)
                message = DateTime.Now.ToString() + " - " + message;




            if (SE_Misc.Flags_HasFlags(m_OutputFlags, (int)OutputModes.LOG_FILE))
            {
                throw new NotImplementedException();
            }
            
            if (SE_Misc.Flags_HasFlags(m_OutputFlags, (int)OutputModes.VS_OUTPUT_PANE))
            {
                Debug.WriteLine(message);
            }
            
            if (SE_Misc.Flags_HasFlags(m_OutputFlags, (int) OutputModes.CONSOLE_WINDOW))
            {
                Console.WriteLine(message);
            }

            if (SE_Misc.Flags_HasFlags(m_OutputFlags, (int)OutputModes.STANDARD_ERROR_STREAM))
            {
                Console.Error.WriteLine(message);
            }

        }

        /// <summary>
        /// This overload of the Log() function takes an extra parameter that lets you specify custom output flags
        /// which will override the current flags that are set in the Logger's OutputMode property.
        /// </summary>
        /// <param name="message">The text to write into the log file as a new line.</param>
        /// <param name="outputFlags">Custom output mode flags.  The flags value passed into this parameter will override the current flags value in the Logger's OutputMode property.</param>
        public void Log(string message, int outputFlags)
        {
            if (outputFlags == (int) OutputModes.NONE)
                return;




            if (m_AddTimeStamps)
                message = DateTime.Now.ToString() + " - " + message;


            if (outputFlags < 1)
                outputFlags = (char)1;
            else if (outputFlags > (char)OutputModes.MAX)
                outputFlags = (char)OutputModes.MAX;


            if (SE_Misc.Flags_HasFlags(m_OutputFlags, (int)OutputModes.LOG_FILE))
            {
                throw new NotImplementedException();
            }

            if (SE_Misc.Flags_HasFlags(m_OutputFlags, (int)OutputModes.VS_OUTPUT_PANE))
            {
                Debug.WriteLine(message);
            }

            if (SE_Misc.Flags_HasFlags(m_OutputFlags, (int)OutputModes.CONSOLE_WINDOW))
            {
                Console.WriteLine(message);
            }

            if (SE_Misc.Flags_HasFlags(m_OutputFlags, (int)OutputModes.STANDARD_ERROR_STREAM))
            {
                Console.Error.WriteLine(message);
            }

        }

        #endregion


        #region Interface Methods

        // This section is for methods that are part of the interfaces that the class implements.

        #region IDisposable Methods

#if USE_IDISPOSABLE

        /// <summary>
        /// This function handles the actual "disposing" of this object.
        /// ANY classes that are inherited from this class, no matter how distantly, MUST override this method so that they can perform their own cleanup!
        /// </summary>
        /// <param name="disposing">This parameter will be true if this function is being called by the public Dispose() method, or false otherwise.</param>
        protected override void Dispose(bool disposing)
        {
            // Check if this object has already been disposed.
            if (SE_Misc.Flags_HasFlags(m_SEObjectFlags, (int)SEObject_Flags.Disposed))
                return;



            if (disposing)
            {
                /*
                * The following text is from MSDN  (http://msdn.microsoft.com/en-us/library/fs2xkftw%28VS.80%29.aspx)
                * 
                * 
                * Dispose(bool disposing) executes in two distinct scenarios:
                * 
                * If disposing equals true, the method has been called directly or indirectly by a user's code and managed and unmanaged resources can be disposed.
                * If disposing equals false, the method has been called by the runtime from inside the finalizer and only unmanaged resources can be disposed. 
                * 
                * When an object is executing its finalization code, it should not reference other objects, because finalizers do not execute in any particular order. 
                * If an executing finalizer references another object that has already been finalized, the executing finalizer will fail.
                */


                // Unregister Events


                // Clean up all managed resources.

            }

            // Clean up all unmanaged resources.



            // Call the base class's Dispose() method if it has one.
            base.Dispose(disposing);
        }

#endif

        #endregion

        #endregion


        #region Event Invocation Methods

        // This section is for methods that invoke the events of this class.  For example:
        //
        // Invoke the Changed event; called whenever list changes
        // protected virtual void OnChanged(EventArgs e) 
        // {
        //		if (Changed != null)
        //			Changed(this, e);
        // }

        #endregion


        #region Operators

        #endregion


        #region Debug Methods

        // This will compile the debug code only if the DEBUG symbol is defined.
        // To define this symbol for all source files in the project, open the Project Properties,
        // and go to the Build tab.  Check the "Define DEBUG constant" checkbox.  With this box checked,
        // the compiler will automatically define the symbol DEBUG.
#if (DEBUG)
        // put all debug methods in here...
#endif

        #endregion


        #region Properties

        /// <summary>
        /// This property determines whether or not the logger will add time stamps to each message.
        /// </summary>
        public bool AddTimeStamps
        {
            get
            {
                return m_AddTimeStamps;
            }
            set
            {
                m_AddTimeStamps = value;
            }
        }

        /// <summary>
        /// Gets a boolean that indicates whether or not logging is enabled.
        /// To disable logging, set the OutputMode flags property to a value of 0.
        /// </summary>
        public bool IsLoggingEnabled
        {
            get
            {
                return m_OutputFlags > 0;
            }
        }

        /// <summary>
        /// This property determines which output modes the logger will use to output messages.
        /// Use the flags in the OutputModes enum to set this property to the ones you want.
        /// You can specify multiple output modes by ORing them together with the | operator.
        /// </summary>
        public char OutputMode
        {
            get
            {
                return m_OutputFlags;
            }
            set
            {
                m_OutputFlags = value;

                if (m_OutputFlags < 0)
                    m_OutputFlags = (char) 0;
                else if (m_OutputFlags > (char) OutputModes.MAX)
                    m_OutputFlags = (char) OutputModes.MAX;
            }
        }

        /// <summary>
        /// Provides access to the object's "Spirit Engine Object" flags.
        /// </summary>
        public override int SEObjectFlags
        {
            get
            {
                return m_SEObjectFlags;
            }
            protected set // Can only be set from within this class and subclasses
            {
                m_SEObjectFlags = value;
            }
        } 

        #endregion

    }
}
